package com.gdufe.firesafe.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.gdufe.firesafe.algorithm.StatisticsFunctions;
import com.gdufe.firesafe.algorithm.Ttest;
import com.gdufe.firesafe.dao.InspectionDao;
import com.gdufe.firesafe.exception.RRException;
import com.gdufe.firesafe.model.constants.RoleConstant;
import com.gdufe.firesafe.model.dto.InspectionDTO;
import com.gdufe.firesafe.model.entity.*;
import com.gdufe.firesafe.model.vo.CurrentUserInspectionVO;
import com.gdufe.firesafe.model.vo.InspectionStatisticsVO;
import com.gdufe.firesafe.service.*;
import com.gdufe.firesafe.utils.*;
import io.github.briqt.spark4j.listener.SparkBaseListener;
import io.github.briqt.spark4j.model.request.SparkRequest;
import io.github.briqt.spark4j.model.response.SparkResponse;
import io.github.briqt.spark4j.model.response.SparkResponseUsage;
import lombok.extern.slf4j.Slf4j;
import okhttp3.WebSocket;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import javax.annotation.Resource;
import java.io.IOException;
import java.io.Serializable;
import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@CacheConfig(cacheNames = "inspection")
@Service("inspectionService")
public class InspectionServiceImpl extends ServiceImpl<InspectionDao, InspectionEntity> implements InspectionService {
    @Resource
    private InspectorService inspectorService;
    @Resource
    private BuildingService buildingService;
    @Resource
    private HouseService houseService;
    @Resource
    private DeviceService deviceService;
    @Resource
    private MessageService messageService;
    @Resource
    private AIRequestUtils aiRequestUtils;

    @Cacheable(key = "#root.methodName+ ':' + #root.args[0]")
    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<InspectionEntity> page = this.page(
                new Query<InspectionEntity>().getPage(params),
                new QueryWrapper<>()
        );

        return new PageUtils(page);
    }

    //根据房屋id获取房屋的本月检查次数
    @Cacheable(key = "#root.methodName+ ':' + #root.args[0]")
    @Override
    public Integer getHouseCountThisMonth(Integer houseId) {
        // 获取本月的起始和结束日期
        LocalDate startDateOfMonth = LocalDate.now().withDayOfMonth(1);
        LocalDate endDateOfMonth = LocalDate.now().withDayOfMonth(LocalDate.now().lengthOfMonth());

        // 构造查询条件
        LambdaQueryWrapper<InspectionEntity> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(InspectionEntity::getHouseId, houseId);
        queryWrapper.between(InspectionEntity::getInspectionDate, startDateOfMonth, endDateOfMonth);

        // 执行查询并返回记录数
        return Math.toIntExact(baseMapper.selectCount(queryWrapper));
    }

    //根据检查员id获取检查员的本月检查次数
    @Cacheable(key = "#root.methodName+ ':' + #root.args[0]")
    @Override
    public Integer getInspectorCountThisMonth(Integer inspectorId) {
        // 获取本月的起始和结束日期
        LocalDate startDateOfMonth = LocalDate.now().withDayOfMonth(1);
        LocalDate endDateOfMonth = LocalDate.now().withDayOfMonth(LocalDate.now().lengthOfMonth());

        // 构造查询条件
        LambdaQueryWrapper<InspectionEntity> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(InspectionEntity::getInspectorId, inspectorId);
        queryWrapper.between(InspectionEntity::getInspectionDate, startDateOfMonth, endDateOfMonth);

        // 执行查询并返回记录数
        return Math.toIntExact(baseMapper.selectCount(queryWrapper));
    }

    //获取最近7次检查的日期
    @Cacheable(key = "#root.methodName+ ':' + #root.args[0]")
    @Override
    public List<Date> get7Dates(Integer houseId) {
        return baseMapper.get7Dates(houseId);
    }

    //获取最近7次检查的分数
    @Cacheable(key = "#root.methodName+ ':' + #root.args[0]")
    @Override
    public List<Double> get7Points(Integer houseId) {
        return baseMapper.get7Points(houseId);
    }

    //由房屋id列表计算近七次检查的平均分列表
    @Cacheable(key = "#root.methodName+ ':' + #root.args[0]")
    @Override
    public List<Double> getAvgScoresByHouseIds(List<Integer> houseIds) {
        //结果
        List<Double> avgScores = new ArrayList<>();
        //某次房屋分数的和
        double[] scoreSums = new double[7];
        Arrays.fill(scoreSums, 0);
        for (Integer houseId : houseIds) {
            // 调用方法获取最近7次检查的分数列表
            List<Double> houseScores = get7Points(houseId);
            for (int i = 0; i < houseScores.size() && i < 7; i++) {
                scoreSums[i] += houseScores.get(i);
            }
        }
        // 将平均分添加到结果列表中
        for (int i = 0; i < 7; i++) {
            avgScores.add(scoreSums[i] / houseIds.size());
        }
        return avgScores;
    }

    //获取检查员统计页数据
    @Cacheable(key = "#root.methodName+ ':' + #root.args[0]")
    @Override
    public InspectionStatisticsVO getInspectionStatistics(Integer inspectorId) {
        InspectorEntity inspector = inspectorService.getById(inspectorId);

        //该检查员负责的房屋列表
        LambdaQueryWrapper<HouseEntity> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(HouseEntity::getBuildingId, inspector.getBuildingId());
        List<HouseEntity> houses = houseService.list(wrapper);

        //该检查员负责的所有房屋id
        List<Integer> houseIds = houses.stream().map(HouseEntity::getId).collect(Collectors.toList());
        //本月各房屋检查次数
        List<Integer> houseInspectionCounts = new ArrayList<>();
        for (Integer houseId : houseIds) {
            houseInspectionCounts.add(this.getHouseCountThisMonth(houseId));
        }
        //各房屋分数
        List<Double> scores = houses.stream().mapToDouble(
                house -> houseService.calculateHousePoint(house.getId())
        ).boxed().collect(Collectors.toList());

        //avgScores需要倒序
        List<Double> avgScores = this.getAvgScoresByHouseIds(houseIds);
        Collections.reverse(avgScores);

        //每月的检查次数
        List<Integer> countPerMonth = this.getCountPerMonth(inspector.getId());
        //把每月的检查次数转换为double类型
        List<Double> doubleList = countPerMonth.stream()
                .mapToDouble(Integer::doubleValue)
                .boxed()
                .collect(Collectors.toList());
        //每月的平均分
        List<Double> avgScoresPerMonth = this.getAvgScoresPerMonth(inspector.getId());
        //计算该检查员每月的检查次数与每月的平均分的皮尔逊相关系数
        double pearsonCorrelationScore = StatisticsFunctions.getPearsonCorrelationScore(doubleList, avgScoresPerMonth);
        List<Double> line = null;
        if (pearsonCorrelationScore >= 0.3) {
            //List转数组
            double[] xData = doubleList.stream().mapToDouble(i -> i).toArray();
            double[] yData = avgScoresPerMonth.stream().mapToDouble(i -> i).toArray();
            //计算该检查员每月的检查次数与每月的平均分的线性回归方程
            line = StatisticsFunctions.leastSquaresRegression(xData, yData);
        }

        //组成散点图坐标
        List<List<Double>> avgScoreFrequencyPoints = new ArrayList<>();
        for (int i = 0; i < countPerMonth.size(); i++) {
            List<Double> list = new ArrayList<>();
            list.add(doubleList.get(i));
            list.add(avgScoresPerMonth.get(i));
            avgScoreFrequencyPoints.add(list);
        }

        //按性别区分的分数
        List<List<Double>> scoresBySex = houseService.getScoresBySex(inspector.getId());
        //List转数组
        double[] manScore = scoresBySex.get(0).stream().mapToDouble(i -> i).toArray();
        double[] womanScore = scoresBySex.get(1).stream().mapToDouble(i -> i).toArray();
        //t检验工具类
        Ttest ttest = new Ttest(manScore, womanScore);

        //构建返回结果
        InspectionStatisticsVO vo = new InspectionStatisticsVO();
        vo.setBuildingName(buildingService.getById(inspector.getBuildingId()).getName());
        vo.setHousesCount(houseService.getHouseNumByBuildingId(inspector.getBuildingId()));
        vo.setInspectorTotalCount(this.getCountByInspectorId(inspector.getId()));
        vo.setThisMonthCount(this.getInspectorCountThisMonth(inspector.getId()));
        vo.setHouseIds(houseIds);
        vo.setHouseInspectionCounts(houseInspectionCounts);
        vo.setScores(scores);
//        vo.setInspectionDates(this.get7Dates());
        vo.setAvgScores(avgScores);
//        vo.setCountsEveryMonth(countPerMonth);
//        vo.setAvgScoresEveryMonth(avgScoresPerMonth);
        vo.setAvgScoreFrequencyPoints(avgScoreFrequencyPoints);
        vo.setPearson(pearsonCorrelationScore);
        vo.setLine(line);
        vo.setAvgScoresBySex(Arrays.asList(ttest.getXMean(), ttest.getYMean()));
        vo.setTValue(ttest.calculateTvalue());
        vo.setPValue(ttest.getPValue());
        vo.setStandards(Arrays.asList(ttest.getStandard(manScore), ttest.getStandard(womanScore)));
        return vo;
    }

    //根据检查员id获取其总检查次数
    @Cacheable(key = "#root.methodName+ ':' + #root.args[0]")
    @Override
    public Integer getCountByInspectorId(Integer inspectorId) {
        LambdaQueryWrapper<InspectionEntity> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(InspectionEntity::getInspectorId, inspectorId);
        return Math.toIntExact(baseMapper.selectCount(queryWrapper));
    }

    //该检查员每月的检查次数
    @Cacheable(key = "#root.methodName+ ':' + #root.args[0]")
    @Override
    public List<Integer> getCountPerMonth(Integer inspectorId) {
        //获取mapList
        List<Map<String, Object>> mapList = baseMapper.getCountPerMonth(inspectorId);
        //将结果转换为List<Integer>
        List<Integer> counts = new ArrayList<>();
        for (Map<String, Object> map : mapList) {
            counts.add(Math.toIntExact((Long) map.get("count")));
        }
        return counts;
    }

    //该检查员每月的平均分
    @Cacheable(key = "#root.methodName+ ':' + #root.args[0]")
    @Override
    public List<Double> getAvgScoresPerMonth(Integer inspectorId) {
        //获取mapList
        List<Map<String, Object>> mapList = baseMapper.getAvgScoresPerMonth(inspectorId);
        //将结果转换为List<Double>
        List<Double> result = new ArrayList<>();
        for (Map<String, Object> map : mapList) {
            result.add((Double) map.get("avg"));
        }
        return result;
    }

    @CacheEvict(allEntries = true)
    @Override
    public void uploadInspection(InspectionDTO inspectionDTO, Integer inspectorId) {
        // 将DTO填充入实体对象
        InspectionEntity inspection = BeanUtil.copyProperties(inspectionDTO, InspectionEntity.class);
        // 设置检查员ID和检查时间
        inspection.setInspectorId(inspectorId);
        inspection.setInspectionDate(new Date());
        //计算总分
        inspection.setResult(inspectionDTO.calculateScore());
        //插入检查记录
        inspection.setId(null);
        baseMapper.insert(inspection);

        //更新房屋得分
        HouseEntity house = BeanUtil.copyProperties(inspectionDTO, HouseEntity.class);
        house.setId(inspectionDTO.getHouseId());
        houseService.updateById(house);

        //系统评估各属性隐患紧急程度，生成消息记录
        MessageEntity messageEntity = messageService.saveByInspection(inspection);

        //找到对应业主的emitter，发送实时消息
        Integer userId = TokenUtils.getCurrentUser().getId();
        SseEmitter emitter = MessageEmitterManager.get(userId);
        try {
            if (emitter != null) {
                emitter.send(messageEntity);
            }
        } catch (IOException e) {
            throw new RRException("发送实时消息失败", e);
        }

        //更新设备可用性和最新检查日期
        //创建实体对象
        DeviceEntity extinguisher = new DeviceEntity();
        DeviceEntity detector = new DeviceEntity();
        //设置id
        extinguisher.setId(house.getFireExtinguisherId());
        detector.setId(house.getSmokeDetectorId());
        //设置设备可用性
        if (!(inspectionDTO.getExtinguisherLeadSealing() &&
                inspectionDTO.getExtinguisherDamaged() &&
                inspectionDTO.getExtinguisherExpired())) {
            extinguisher.setAvailable(false);
        }
        if (!inspectionDTO.getDetectorNoError()) {
            detector.setAvailable(false);
        }
        //设置最新检查时间
        extinguisher.setLastInspectionDate(new Date());
        detector.setLastInspectionDate(new Date());
        //更新设备信息
        deviceService.updateById(extinguisher);
        deviceService.updateById(detector);
//        DeviceEntity extinguisher = deviceService.getById(house.getFireExtinguisherId());
//        DeviceEntity detector = deviceService.getById(house.getSmokeDetectorId());
    }

    //获取单个检查员的检查记录
    @Override
    @Cacheable(key = "#root.methodName+ ':' + #root.args[0]")
    public PageUtils getCurrentUserInspection(Map<String, Object> params) {
        QueryWrapper<CurrentUserInspectionVO> queryWrapper = new QueryWrapper<>();
        UserEntity currentUser = TokenUtils.getCurrentUser();
        Integer roleId = currentUser.getRoleId();
        if (RoleConstant.OWNER.equals(currentUser.getRole())) {
            queryWrapper.eq("h.owner_id", roleId);
        } else if(RoleConstant.INSPECTOR.equals(currentUser.getRole())) {
            queryWrapper.eq("ior.id", roleId);
        }
        String key = (String) params.get("key");
        queryWrapper.and(wrapper -> wrapper.eq("ion.id", key).or().like("ior.name", key)
                .or().like("ion.inspection_date", key).or().like("h.address", key)
                .or().like("ion.result", key));

        IPage<CurrentUserInspectionVO> res = baseMapper
                .getCurrentUserInspection(new Query<CurrentUserInspectionVO>().getPage(params), queryWrapper);
        return new PageUtils(res);
    }

    @Override
    public SseEmitter getOwnerAISuggestion(Integer inspectionId) {
        //当前业主的最新房屋检查记录
        InspectionEntity inspection = this.getById(inspectionId);
//        Integer ownerId = TokenUtils.getCurrentUser().getRoleId();
//        Integer houseId = houseService.lambdaQuery().eq(HouseEntity::getOwnerId, ownerId).one().getId();
//        InspectionEntity inspection = this.lambdaQuery().eq(InspectionEntity::getHouseId, houseId).orderByDesc(InspectionEntity::getInspectionDate).last("LIMIT 1").one();
        //提示词
        String prompt = "你是一位消防安全专家，以下是一次房屋安全检查数据，每一行是数据的解释和对应的数字，除了通道疏通情况，其他数字1表示true，0表示false，请你给这次检查做出建议，禁止使用markdown格式返回\n" +
                "通道疏通情况，等级0-3，越高越通畅" + inspection.getPassageUnblockStatus() + "\n" +
                "烟雾检测器电池电量充足 " + inspection.getDetectorElectricity() + "\n" +
                "烟雾检测器自检功能正常 " + inspection.getDetectorSelfTest() + "\n" +
                "烟雾检测器无故障报警记录 " + inspection.getDetectorNoError() + "\n" +
                "燃气管道无泄漏 " + inspection.getGasNoLeakage() + "\n" +
                "燃气表读数正常 " + inspection.getGasMeterStatus() + "\n" +
                "自动切断阀功能有效 " + inspection.getGasAutomaticShutOff() + "\n" +
                "电路无故障 " + inspection.getCircuitStatus() + "\n" +
                "电气设备绝缘良好 " + inspection.getElectricalInsulation() + "\n" +
                "备用电源工作正常 " + inspection.getStandbyPower() + "\n" +
                "负载分配合理 " + inspection.getElectricalLoad() + "\n" +
                "接地保护完善 " + inspection.getElectricalGrounding() + "\n" +
                "灭火器铅封完好 " + inspection.getExtinguisherLeadSealing() + "\n" +
                "灭火器压力良好 " + inspection.getExtinguisherPressure() + "\n" +
                "灭火器无明显物理损坏 " + inspection.getExtinguisherDamaged() + "\n" +
                "灭火器未过期 " + inspection.getExtinguisherExpired() + "\n";
        //创建不会自动关闭的SSE发射器
        SseEmitter emitter = new SseEmitter(0L);
        //模拟调用AI接口，返回结果
//        final StringBuilder res = new StringBuilder();
//        new Thread(() -> {
//            for (int i = 0; i < 5; i++) {
//                try {
//                    String content = "11111111111111111111111111111111\n\n";
//                    res.append(content);
//                    emitter.send(R.ok(content));
//                    Thread.sleep(2000);
//                } catch (IOException | InterruptedException e) {
//                    throw new RRException("业主房屋检查记录AI评价发送消息失败");
//                }
//            }
//            inspection.setAiSuggestion(res.toString());
//            this.updateById(inspection);
//        }).start();

        //调用AI接口，返回结果
        aiRequestUtils.streamRequest(prompt, new SparkBaseListener(){
            private final StringBuilder res = new StringBuilder();
            @Override
            public void onMessage(String content, SparkResponseUsage usage, Integer status, SparkRequest sparkRequest, SparkResponse sparkResponse, WebSocket webSocket) {
                try {
                    emitter.send(R.ok(content));
                } catch (IOException e) {
                    throw new RRException("业主房屋检查记录AI评价发送消息失败");
                }
                res.append(content);
                //status 0代表首个文本结果；1代表中间文本结果；2代表最后一个文本结果
                if (2 == status) {
                    emitter.complete();
                    log.info("AI完整回答\n{}", res.toString());
                    inspection.setAiSuggestion(res.toString());
                    updateById(inspection);
                }
            }
        });
        return emitter;
    }

//    @Override
//    public List<InspectionEntity> getOwnerInspection() {
//        Integer ownerId = TokenUtils.getCurrentUser().getRoleId();
//        HouseEntity house = houseService.lambdaQuery().eq(HouseEntity::getOwnerId, ownerId).one();
//        return this.lambdaQuery().eq(InspectionEntity::getHouseId, house.getId()).list();
//    }

    @CacheEvict(allEntries = true)
    @Override
    public boolean save(InspectionEntity entity) {
        return super.save(entity);
    }

    @CacheEvict(allEntries = true)
    @Override
    public boolean removeByIds(Collection<? extends Serializable> idList) {
        return super.removeByIds(idList);
    }

    @CacheEvict(allEntries = true)
    @Override
    public boolean updateById(InspectionEntity entity) {
        return super.updateById(entity);
    }

    @Cacheable(key = "#root.methodName+ ':' + #root.args[0]")
    @Override
    public InspectionEntity getById(Serializable id) {
        return super.getById(id);
    }
}
